home *** CD-ROM | disk | FTP | other *** search
/ PC go! 2008 October / PCgo 2008-10 (DVD).iso / interface / contents / vollversionen_6617 / 21733 / files / xulrunner / components / nsHandlerService.js < prev    next >
Encoding:
Text File  |  2008-08-20  |  50.0 KB  |  1,369 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is the Mozilla browser.
  15.  *
  16.  * The Initial Developer of the Original Code is Mozilla.
  17.  * Portions created by the Initial Developer are Copyright (C) 2007
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Myk Melez <myk@mozilla.org>
  22.  *   Dan Mosedale <dmose@mozilla.org>
  23.  *   Florian Queze <florian@queze.net>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. const Ci = Components.interfaces;
  40. const Cc = Components.classes;
  41. const Cu = Components.utils;
  42. const Cr = Components.results;
  43.  
  44.  
  45. const CLASS_MIMEINFO        = "mimetype";
  46. const CLASS_PROTOCOLINFO    = "scheme";
  47.  
  48.  
  49. // namespace prefix
  50. const NC_NS                 = "http://home.netscape.com/NC-rdf#";
  51.  
  52. // the most recent default handlers that have been injected.  Note that
  53. // this is used to construct an RDF resource, which needs to have NC_NS
  54. // prepended, since that hasn't been done yet
  55. const DEFAULT_HANDLERS_VERSION = "defaultHandlersVersion";
  56.  
  57. // type list properties
  58.  
  59. const NC_MIME_TYPES         = NC_NS + "MIME-types";
  60. const NC_PROTOCOL_SCHEMES   = NC_NS + "Protocol-Schemes";
  61.  
  62. // content type ("type") properties
  63.  
  64. // nsIHandlerInfo::type
  65. const NC_VALUE              = NC_NS + "value";
  66. const NC_DESCRIPTION        = NC_NS + "description";
  67.  
  68. // additional extensions
  69. const NC_FILE_EXTENSIONS    = NC_NS + "fileExtensions";
  70.  
  71. // references nsIHandlerInfo record
  72. const NC_HANDLER_INFO       = NC_NS + "handlerProp";
  73.  
  74. // handler info ("info") properties
  75.  
  76. // nsIHandlerInfo::preferredAction
  77. const NC_SAVE_TO_DISK       = NC_NS + "saveToDisk";
  78. const NC_HANDLE_INTERNALLY  = NC_NS + "handleInternal";
  79. const NC_USE_SYSTEM_DEFAULT = NC_NS + "useSystemDefault";
  80.  
  81. // nsIHandlerInfo::alwaysAskBeforeHandling
  82. const NC_ALWAYS_ASK         = NC_NS + "alwaysAsk";
  83.  
  84. // references nsIHandlerApp records
  85. const NC_PREFERRED_APP      = NC_NS + "externalApplication";
  86. const NC_POSSIBLE_APP       = NC_NS + "possibleApplication";
  87.  
  88. // handler app ("handler") properties
  89.  
  90. // nsIHandlerApp::name
  91. const NC_PRETTY_NAME        = NC_NS + "prettyName";
  92.  
  93. // nsILocalHandlerApp::executable
  94. const NC_PATH               = NC_NS + "path";
  95.  
  96. // nsIWebHandlerApp::uriTemplate
  97. const NC_URI_TEMPLATE       = NC_NS + "uriTemplate";
  98.  
  99.  
  100. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  101.  
  102.  
  103. function HandlerService() {
  104.   this._init();
  105. }
  106.  
  107. HandlerService.prototype = {
  108.   //**************************************************************************//
  109.   // XPCOM Plumbing
  110.  
  111.   classDescription: "Handler Service",
  112.   classID:          Components.ID("{32314cc8-22f7-4f7f-a645-1a45453ba6a6}"),
  113.   contractID:       "@mozilla.org/uriloader/handler-service;1",
  114.   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIHandlerService]),
  115.  
  116.  
  117.   //**************************************************************************//
  118.   // Initialization & Destruction
  119.   
  120.   _init: function HS__init() {
  121.     // Observe profile-before-change so we can switch to the datasource
  122.     // in the new profile when the user changes profiles.
  123.     this._observerSvc.addObserver(this, "profile-before-change", false);
  124.  
  125.     // Observe xpcom-shutdown so we can remove these observers
  126.     // when the application shuts down.
  127.     this._observerSvc.addObserver(this, "xpcom-shutdown", false);
  128.  
  129.     // Observe profile-do-change so that non-default profiles get upgraded too
  130.     this._observerSvc.addObserver(this, "profile-do-change", false);
  131.     
  132.     // do any necessary updating of the datastore
  133.     this._updateDB();
  134.   },
  135.  
  136.   _updateDB: function HS__updateDB() {
  137.     try {
  138.       var defaultHandlersVersion = this._datastoreDefaultHandlersVersion;
  139.     } catch(ex) {
  140.       // accessing the datastore failed, we can't update anything
  141.       return;
  142.     }
  143.  
  144.     try {
  145.       // if we don't have the current version of the default prefs for
  146.       // this locale, inject any new default handers into the datastore
  147.       if (defaultHandlersVersion < this._prefsDefaultHandlersVersion) {
  148.  
  149.         // set the new version first so that if we recurse we don't
  150.         // call _injectNewDefaults several times
  151.         this._datastoreDefaultHandlersVersion =
  152.           this._prefsDefaultHandlersVersion;
  153.         this._injectNewDefaults();
  154.       } 
  155.     } catch (ex) {
  156.       // if injecting the defaults failed, set the version back to the
  157.       // previous value
  158.       this._datastoreDefaultHandlersVersion = defaultHandlersVersion;
  159.     }
  160.   },
  161.  
  162.   get _currentLocale() {
  163.     var chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"].
  164.                          getService(Ci.nsIXULChromeRegistry);
  165.     var currentLocale = chromeRegistry.getSelectedLocale("global");
  166.     return currentLocale;
  167.   }, 
  168.  
  169.   _destroy: function HS__destroy() {
  170.     this._observerSvc.removeObserver(this, "profile-before-change");
  171.     this._observerSvc.removeObserver(this, "xpcom-shutdown");
  172.     this._observerSvc.removeObserver(this, "profile-do-change");
  173.  
  174.     // XXX Should we also null references to all the services that get stored
  175.     // by our memoizing getters in the Convenience Getters section?
  176.   },
  177.  
  178.   _onProfileChange: function HS__onProfileChange() {
  179.     // Lose our reference to the datasource so we reacquire it
  180.     // from the new profile the next time we need it.
  181.     this.__ds = null;
  182.   },
  183.  
  184.   _isInHandlerArray: function HS__isInHandlerArray(aArray, aHandler) {
  185.     var enumerator = aArray.enumerate();
  186.     while (enumerator.hasMoreElements()) {
  187.       let handler = enumerator.getNext();
  188.       handler.QueryInterface(Ci.nsIHandlerApp);
  189.       if (handler.equals(aHandler))
  190.         return true;
  191.     }
  192.     
  193.     return false;
  194.   },
  195.  
  196.   // note that this applies to the current locale only 
  197.   get _datastoreDefaultHandlersVersion() {
  198.     var version = this._getValue("urn:root", NC_NS + this._currentLocale +
  199.                                              "_" + DEFAULT_HANDLERS_VERSION);
  200.     
  201.     return version ? version : -1;
  202.   },
  203.  
  204.   set _datastoreDefaultHandlersVersion(aNewVersion) {
  205.     return this._setLiteral("urn:root", NC_NS + this._currentLocale + "_" + 
  206.                             DEFAULT_HANDLERS_VERSION, aNewVersion);
  207.   },
  208.  
  209.   get _prefsDefaultHandlersVersion() {
  210.     // get handler service pref branch
  211.     var prefSvc = Cc["@mozilla.org/preferences-service;1"].
  212.                   getService(Ci.nsIPrefService);
  213.     var handlerSvcBranch = prefSvc.getBranch("gecko.handlerService.");
  214.  
  215.     // get the version of the preferences for this locale
  216.     return Number(handlerSvcBranch.
  217.                   getComplexValue("defaultHandlersVersion", 
  218.                                   Ci.nsIPrefLocalizedString).data);
  219.   },
  220.   
  221.   _injectNewDefaults: function HS__injectNewDefaults() {
  222.     // get handler service pref branch
  223.     var prefSvc = Cc["@mozilla.org/preferences-service;1"].
  224.                   getService(Ci.nsIPrefService);
  225.  
  226.     let schemesPrefBranch = prefSvc.getBranch("gecko.handlerService.schemes.");
  227.     let schemePrefList = schemesPrefBranch.getChildList("", {}); 
  228.  
  229.     var schemes = {};
  230.  
  231.     // read all the scheme prefs into a hash
  232.     for each (var schemePrefName in schemePrefList) {
  233.  
  234.       let [scheme, handlerNumber, attribute] = schemePrefName.split(".");
  235.  
  236.       try {
  237.         var attrData =
  238.           schemesPrefBranch.getComplexValue(schemePrefName,
  239.                                             Ci.nsIPrefLocalizedString).data;
  240.         if (!(scheme in schemes))
  241.           schemes[scheme] = {};
  242.   
  243.         if (!(handlerNumber in schemes[scheme]))
  244.           schemes[scheme][handlerNumber] = {};
  245.         
  246.         schemes[scheme][handlerNumber][attribute] = attrData;
  247.       } catch (ex) {}
  248.     }
  249.  
  250.     let protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
  251.                    getService(Ci.nsIExternalProtocolService);
  252.     for (var scheme in schemes) {
  253.  
  254.       // This clause is essentially a reimplementation of 
  255.       // nsIExternalProtocolHandlerService.getProtocolHandlerInfo().
  256.       // Necessary because calling that from here would make XPConnect barf
  257.       // when getService tried to re-enter the constructor for this
  258.       // service.
  259.       let osDefaultHandlerFound = {};
  260.       let protoInfo = protoSvc.getProtocolHandlerInfoFromOS(scheme, 
  261.                                osDefaultHandlerFound);
  262.       
  263.       if (this.exists(protoInfo))
  264.         this.fillHandlerInfo(protoInfo, null);
  265.       else
  266.         protoSvc.setProtocolHandlerDefaults(protoInfo, 
  267.                                             osDefaultHandlerFound.value);
  268.  
  269.       // cache the possible handlers to avoid extra xpconnect traversals.      
  270.       let possibleHandlers = protoInfo.possibleApplicationHandlers;
  271.  
  272.       for each (var handlerPrefs in schemes[scheme]) {
  273.  
  274.         let handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"].
  275.                          createInstance(Ci.nsIWebHandlerApp);
  276.  
  277.         handlerApp.uriTemplate = handlerPrefs.uriTemplate;
  278.         handlerApp.name = handlerPrefs.name;                
  279.  
  280.         if (!this._isInHandlerArray(possibleHandlers, handlerApp)) {
  281.           possibleHandlers.appendElement(handlerApp, false);
  282.         }
  283.       }
  284.  
  285.       this.store(protoInfo);
  286.     }
  287.   },
  288.  
  289.   //**************************************************************************//
  290.   // nsIObserver
  291.   
  292.   observe: function HS__observe(subject, topic, data) {
  293.     switch(topic) {
  294.       case "profile-before-change":
  295.         this._onProfileChange();
  296.         break;
  297.       case "xpcom-shutdown":
  298.         this._destroy();
  299.         break;
  300.       case "profile-do-change":
  301.         this._updateDB();
  302.         break;  
  303.     }
  304.   },
  305.  
  306.  
  307.   //**************************************************************************//
  308.   // nsIHandlerService
  309.  
  310.   enumerate: function HS_enumerate() {
  311.     var handlers = Cc["@mozilla.org/array;1"].
  312.                    createInstance(Ci.nsIMutableArray);
  313.     this._appendHandlers(handlers, CLASS_MIMEINFO);
  314.     this._appendHandlers(handlers, CLASS_PROTOCOLINFO);
  315.     return handlers.enumerate();
  316.   },
  317.  
  318.   fillHandlerInfo: function HS_fillHandlerInfo(aHandlerInfo, aOverrideType) {
  319.     var type = aOverrideType || aHandlerInfo.type;
  320.     var typeID = this._getTypeID(this._getClass(aHandlerInfo), type);
  321.  
  322.     // Determine whether or not information about this handler is available
  323.     // in the datastore by looking for its "value" property, which stores its
  324.     // type and should always be present.
  325.     if (!this._hasValue(typeID, NC_VALUE))
  326.       throw Cr.NS_ERROR_NOT_AVAILABLE;
  327.  
  328.     // Retrieve the human-readable description of the type.
  329.     if (this._hasValue(typeID, NC_DESCRIPTION))
  330.       aHandlerInfo.description = this._getValue(typeID, NC_DESCRIPTION);
  331.  
  332.     // Note: for historical reasons, we don't actually check that the type
  333.     // record has a "handlerProp" property referencing the info record.  It's
  334.     // unclear whether or not we should start doing this check; perhaps some
  335.     // legacy datasources don't have such references.
  336.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), type);
  337.  
  338.     aHandlerInfo.preferredAction = this._retrievePreferredAction(infoID);
  339.  
  340.     var preferredHandlerID =
  341.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), type);
  342.  
  343.     // Retrieve the preferred handler.
  344.     // Note: for historical reasons, we don't actually check that the info
  345.     // record has an "externalApplication" property referencing the preferred
  346.     // handler record.  It's unclear whether or not we should start doing
  347.     // this check; perhaps some legacy datasources don't have such references.
  348.     aHandlerInfo.preferredApplicationHandler =
  349.       this._retrieveHandlerApp(preferredHandlerID);
  350.  
  351.     // Fill the array of possible handlers with the ones in the datastore.
  352.     this._fillPossibleHandlers(infoID,
  353.                                aHandlerInfo.possibleApplicationHandlers,
  354.                                aHandlerInfo.preferredApplicationHandler);
  355.  
  356.     // If we have an "always ask" flag stored in the RDF, always use its
  357.     // value. Otherwise, use the default value stored in the pref service.
  358.     var alwaysAsk;
  359.     if (this._hasValue(infoID, NC_ALWAYS_ASK)) {
  360.       alwaysAsk = (this._getValue(infoID, NC_ALWAYS_ASK) != "false");
  361.     } else {
  362.       var prefSvc = Cc["@mozilla.org/preferences-service;1"].
  363.                     getService(Ci.nsIPrefService);
  364.       var prefBranch = prefSvc.getBranch("network.protocol-handler.");
  365.       try {
  366.         alwaysAsk = prefBranch.getBoolPref("warn-external." + type);
  367.       } catch (e) {
  368.         // will throw if pref didn't exist.
  369.         try {
  370.           alwaysAsk = prefBranch.getBoolPref("warn-external-default");
  371.         } catch (e) {
  372.           // Nothing to tell us what to do, so be paranoid and prompt.
  373.           alwaysAsk = true;
  374.         }
  375.       }
  376.     }
  377.     aHandlerInfo.alwaysAskBeforeHandling = alwaysAsk;
  378.  
  379.     // If the object represents a MIME type handler, then also retrieve
  380.     // any file extensions.
  381.     if (aHandlerInfo instanceof Ci.nsIMIMEInfo)
  382.       for each (let fileExtension in this._retrieveFileExtensions(typeID))
  383.         aHandlerInfo.appendExtension(fileExtension);
  384.   },
  385.  
  386.   store: function HS_store(aHandlerInfo) {
  387.     // FIXME: when we switch from RDF to something with transactions (like
  388.     // SQLite), enclose the following changes in a transaction so they all
  389.     // get rolled back if any of them fail and we don't leave the datastore
  390.     // in an inconsistent state.
  391.  
  392.     this._ensureRecordsForType(aHandlerInfo);
  393.     this._storePreferredAction(aHandlerInfo);
  394.     this._storePreferredHandler(aHandlerInfo);
  395.     this._storePossibleHandlers(aHandlerInfo);
  396.     this._storeAlwaysAsk(aHandlerInfo);
  397.  
  398.     // Write the changes to the database immediately so we don't lose them
  399.     // if the application crashes.
  400.     if (this._ds instanceof Ci.nsIRDFRemoteDataSource)
  401.       this._ds.Flush();
  402.   },
  403.  
  404.   exists: function HS_exists(aHandlerInfo) {
  405.     var found;
  406.  
  407.     try {
  408.       var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  409.       found = this._hasLiteralAssertion(typeID, NC_VALUE, aHandlerInfo.type);
  410.     } catch (e) {
  411.       // If the RDF threw (eg, corrupt file), treat as non-existent.
  412.       found = false;
  413.     }
  414.  
  415.     return found;
  416.   },
  417.  
  418.   remove: function HS_remove(aHandlerInfo) {
  419.     var preferredHandlerID =
  420.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  421.     this._removeAssertions(preferredHandlerID);
  422.  
  423.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  424.  
  425.     // Get a list of possible handlers.  After we have removed the info record,
  426.     // we'll check if any other info records reference these handlers, and we'll
  427.     // remove the handler records that aren't referenced by other info records.
  428.     var possibleHandlerIDs = [];
  429.     var possibleHandlerTargets = this._getTargets(infoID, NC_POSSIBLE_APP);
  430.     while (possibleHandlerTargets.hasMoreElements()) {
  431.       let possibleHandlerTarget = possibleHandlerTargets.getNext();
  432.       // Note: possibleHandlerTarget should always be an nsIRDFResource.
  433.       // The conditional is just here in case of a corrupt RDF datasource.
  434.       if (possibleHandlerTarget instanceof Ci.nsIRDFResource)
  435.         possibleHandlerIDs.push(possibleHandlerTarget.ValueUTF8);
  436.     }
  437.  
  438.     // Remove the info record.
  439.     this._removeAssertions(infoID);
  440.  
  441.     // Now that we've removed the info record, remove any possible handlers
  442.     // that aren't referenced by other info records.
  443.     for each (let possibleHandlerID in possibleHandlerIDs)
  444.       if (!this._existsResourceTarget(NC_POSSIBLE_APP, possibleHandlerID))
  445.         this._removeAssertions(possibleHandlerID);
  446.  
  447.     var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  448.     this._removeAssertions(typeID);
  449.  
  450.     // Now that there's no longer a handler for this type, remove the type
  451.     // from the list of types for which there are known handlers.
  452.     var typeList = this._ensureAndGetTypeList(this._getClass(aHandlerInfo));
  453.     var type = this._rdf.GetResource(typeID);
  454.     var typeIndex = typeList.IndexOf(type);
  455.     if (typeIndex != -1)
  456.       typeList.RemoveElementAt(typeIndex, true);
  457.  
  458.     // Write the changes to the database immediately so we don't lose them
  459.     // if the application crashes.
  460.     // XXX If we're removing a bunch of handlers at once, will flushing
  461.     // after every removal cause a significant performance hit?
  462.     if (this._ds instanceof Ci.nsIRDFRemoteDataSource)
  463.       this._ds.Flush();
  464.   },
  465.  
  466.   getTypeFromExtension: function HS_getTypeFromExtension(aFileExtension) {
  467.     var fileExtension = aFileExtension.toLowerCase();
  468.     var typeID;
  469.  
  470.     if (this._existsLiteralTarget(NC_FILE_EXTENSIONS, fileExtension))
  471.       typeID = this._getSourceForLiteral(NC_FILE_EXTENSIONS, fileExtension);
  472.  
  473.     if (typeID && this._hasValue(typeID, NC_VALUE)) {
  474.       let type = this._getValue(typeID, NC_VALUE);
  475.       if (type == "")
  476.         throw Cr.NS_ERROR_FAILURE;
  477.       return type;
  478.     }
  479.  
  480.     return "";
  481.   },
  482.  
  483.  
  484.   //**************************************************************************//
  485.   // Retrieval Methods
  486.  
  487.   /**
  488.    * Retrieve the preferred action for the info record with the given ID.
  489.    *
  490.    * @param aInfoID  {string}  the info record ID
  491.    *
  492.    * @returns  {integer}  the preferred action enumeration value
  493.    */
  494.   _retrievePreferredAction: function HS__retrievePreferredAction(aInfoID) {
  495.     if (this._getValue(aInfoID, NC_SAVE_TO_DISK) == "true")
  496.       return Ci.nsIHandlerInfo.saveToDisk;
  497.     
  498.     if (this._getValue(aInfoID, NC_USE_SYSTEM_DEFAULT) == "true")
  499.       return Ci.nsIHandlerInfo.useSystemDefault;
  500.     
  501.     if (this._getValue(aInfoID, NC_HANDLE_INTERNALLY) == "true")
  502.       return Ci.nsIHandlerInfo.handleInternal;
  503.  
  504.     return Ci.nsIHandlerInfo.useHelperApp;
  505.   },
  506.  
  507.   /**
  508.    * Fill an array of possible handlers with the handlers for the given info ID.
  509.    *
  510.    * @param aInfoID            {string}           the ID of the info record
  511.    * @param aPossibleHandlers  {nsIMutableArray}  the array of possible handlers
  512.    * @param aPreferredHandler  {nsIHandlerApp}    the preferred handler, if any
  513.    */
  514.   _fillPossibleHandlers: function HS__fillPossibleHandlers(aInfoID,
  515.                                                            aPossibleHandlers,
  516.                                                            aPreferredHandler) {
  517.     // The set of possible handlers should include the preferred handler,
  518.     // but legacy datastores (from before we added possible handlers) won't
  519.     // include the preferred handler, so check if it's included as we build
  520.     // the list of handlers, and, if it's not included, add it to the list.
  521.     if (aPreferredHandler)
  522.       aPossibleHandlers.appendElement(aPreferredHandler, false);
  523.  
  524.     var possibleHandlerTargets = this._getTargets(aInfoID, NC_POSSIBLE_APP);
  525.  
  526.     while (possibleHandlerTargets.hasMoreElements()) {
  527.       let possibleHandlerTarget = possibleHandlerTargets.getNext();
  528.       if (!(possibleHandlerTarget instanceof Ci.nsIRDFResource))
  529.         continue;
  530.  
  531.       let possibleHandlerID = possibleHandlerTarget.ValueUTF8;
  532.       let possibleHandler = this._retrieveHandlerApp(possibleHandlerID);
  533.       if (possibleHandler && (!aPreferredHandler ||
  534.                               !possibleHandler.equals(aPreferredHandler)))
  535.         aPossibleHandlers.appendElement(possibleHandler, false);
  536.     }
  537.   },
  538.  
  539.   /**
  540.    * Retrieve the handler app object with the given ID.
  541.    *
  542.    * @param aHandlerAppID  {string}  the ID of the handler app to retrieve
  543.    *
  544.    * @returns  {nsIHandlerApp}  the handler app, if any; otherwise null
  545.    */
  546.   _retrieveHandlerApp: function HS__retrieveHandlerApp(aHandlerAppID) {
  547.     var handlerApp;
  548.  
  549.     // If it has a path, it's a local handler; otherwise, it's a web handler.
  550.     if (this._hasValue(aHandlerAppID, NC_PATH)) {
  551.       let executable =
  552.         this._getFileWithPath(this._getValue(aHandlerAppID, NC_PATH));
  553.       if (!executable)
  554.         return null;
  555.  
  556.       handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
  557.                    createInstance(Ci.nsILocalHandlerApp);
  558.       handlerApp.executable = executable;
  559.     }
  560.     else if (this._hasValue(aHandlerAppID, NC_URI_TEMPLATE)) {
  561.       let uriTemplate = this._getValue(aHandlerAppID, NC_URI_TEMPLATE);
  562.       if (!uriTemplate)
  563.         return null;
  564.  
  565.       handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"].
  566.                    createInstance(Ci.nsIWebHandlerApp);
  567.       handlerApp.uriTemplate = uriTemplate;
  568.     }
  569.     else
  570.       return null;
  571.  
  572.     handlerApp.name = this._getValue(aHandlerAppID, NC_PRETTY_NAME);
  573.  
  574.     return handlerApp;
  575.   },
  576.  
  577.   /*
  578.    * Retrieve file extensions, if any, for the MIME type with the given type ID.
  579.    *
  580.    * @param aTypeID  {string}  the type record ID
  581.    */
  582.   _retrieveFileExtensions: function HS__retrieveFileExtensions(aTypeID) {
  583.     var fileExtensions = [];
  584.  
  585.     var fileExtensionTargets = this._getTargets(aTypeID, NC_FILE_EXTENSIONS);
  586.  
  587.     while (fileExtensionTargets.hasMoreElements()) {
  588.       let fileExtensionTarget = fileExtensionTargets.getNext();
  589.       if (fileExtensionTarget instanceof Ci.nsIRDFLiteral &&
  590.           fileExtensionTarget.Value != "")
  591.         fileExtensions.push(fileExtensionTarget.Value);
  592.     }
  593.  
  594.     return fileExtensions;
  595.   },
  596.  
  597.   /**
  598.    * Get the file with the given path.  This is not as simple as merely
  599.    * initializing a local file object with the path, because the path might be
  600.    * relative to the current process directory, in which case we have to
  601.    * construct a path starting from that directory.
  602.    *
  603.    * @param aPath  {string}  a path to a file
  604.    *
  605.    * @returns {nsILocalFile} the file, or null if the file does not exist
  606.    */
  607.   _getFileWithPath: function HS__getFileWithPath(aPath) {
  608.     var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  609.  
  610.     try {
  611.       file.initWithPath(aPath);
  612.  
  613.       if (file.exists())
  614.         return file;
  615.     }
  616.     catch(ex) {
  617.       // Note: for historical reasons, we don't actually check to see
  618.       // if the exception is NS_ERROR_FILE_UNRECOGNIZED_PATH, which is what
  619.       // nsILocalFile::initWithPath throws when a path is relative.
  620.  
  621.       file = this._dirSvc.get("XCurProcD", Ci.nsIFile);
  622.  
  623.       try {
  624.         file.append(aPath);
  625.         if (file.exists())
  626.           return file;
  627.       }
  628.       catch(ex) {}
  629.     }
  630.  
  631.     return null;
  632.   },
  633.  
  634.  
  635.   //**************************************************************************//
  636.   // Storage Methods
  637.  
  638.   _storePreferredAction: function HS__storePreferredAction(aHandlerInfo) {
  639.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  640.  
  641.     switch(aHandlerInfo.preferredAction) {
  642.       case Ci.nsIHandlerInfo.saveToDisk:
  643.         this._setLiteral(infoID, NC_SAVE_TO_DISK, "true");
  644.         this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
  645.         this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
  646.         break;
  647.  
  648.       case Ci.nsIHandlerInfo.handleInternally:
  649.         this._setLiteral(infoID, NC_HANDLE_INTERNALLY, "true");
  650.         this._removeTarget(infoID, NC_SAVE_TO_DISK);
  651.         this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
  652.         break;
  653.  
  654.       case Ci.nsIHandlerInfo.useSystemDefault:
  655.         this._setLiteral(infoID, NC_USE_SYSTEM_DEFAULT, "true");
  656.         this._removeTarget(infoID, NC_SAVE_TO_DISK);
  657.         this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
  658.         break;
  659.  
  660.       // This value is indicated in the datastore either by the absence of
  661.       // the three properties or by setting them all "false".  Of these two
  662.       // options, the former seems preferable, because it reduces the size
  663.       // of the RDF file and thus the amount of stuff we have to parse.
  664.       case Ci.nsIHandlerInfo.useHelperApp:
  665.       default:
  666.         this._removeTarget(infoID, NC_SAVE_TO_DISK);
  667.         this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
  668.         this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
  669.         break;
  670.     }
  671.   },
  672.  
  673.   _storePreferredHandler: function HS__storePreferredHandler(aHandlerInfo) {
  674.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  675.     var handlerID =
  676.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  677.  
  678.     var handler = aHandlerInfo.preferredApplicationHandler;
  679.  
  680.     if (handler) {
  681.       this._storeHandlerApp(handlerID, handler);
  682.  
  683.       // Make this app be the preferred app for the handler info.
  684.       //
  685.       // Note: nsExternalHelperAppService::FillContentHandlerProperties ignores
  686.       // this setting and instead identifies the preferred app as the resource
  687.       // whose URI follows the pattern urn:<class>:externalApplication:<type>.
  688.       // But the old downloadactions.js code used to set this property, so just
  689.       // in case there is still some code somewhere that relies on its presence,
  690.       // we set it here.
  691.       this._setResource(infoID, NC_PREFERRED_APP, handlerID);
  692.     }
  693.     else {
  694.       // There isn't a preferred handler.  Remove the existing record for it,
  695.       // if any.
  696.       this._removeTarget(infoID, NC_PREFERRED_APP);
  697.       this._removeAssertions(handlerID);
  698.     }
  699.   },
  700.  
  701.   /**
  702.    * Store the list of possible handler apps for the content type represented
  703.    * by the given handler info object.
  704.    *
  705.    * @param aHandlerInfo  {nsIHandlerInfo}  the handler info object
  706.    */
  707.   _storePossibleHandlers: function HS__storePossibleHandlers(aHandlerInfo) {
  708.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  709.  
  710.     // First, retrieve the set of handler apps currently stored for the type,
  711.     // keeping track of their IDs in a hash that we'll use to determine which
  712.     // ones are no longer valid and should be removed.
  713.     var currentHandlerApps = {};
  714.     var currentHandlerTargets = this._getTargets(infoID, NC_POSSIBLE_APP);
  715.     while (currentHandlerTargets.hasMoreElements()) {
  716.       let handlerApp = currentHandlerTargets.getNext();
  717.       if (handlerApp instanceof Ci.nsIRDFResource) {
  718.         let handlerAppID = handlerApp.ValueUTF8;
  719.         currentHandlerApps[handlerAppID] = true;
  720.       }
  721.     }
  722.  
  723.     // Next, store any new handler apps.
  724.     var newHandlerApps =
  725.       aHandlerInfo.possibleApplicationHandlers.enumerate();
  726.     while (newHandlerApps.hasMoreElements()) {
  727.       let handlerApp =
  728.         newHandlerApps.getNext().QueryInterface(Ci.nsIHandlerApp);
  729.       let handlerAppID = this._getPossibleHandlerAppID(handlerApp);
  730.       if (!this._hasResourceAssertion(infoID, NC_POSSIBLE_APP, handlerAppID)) {
  731.         this._storeHandlerApp(handlerAppID, handlerApp);
  732.         this._addResourceAssertion(infoID, NC_POSSIBLE_APP, handlerAppID);
  733.       }
  734.       delete currentHandlerApps[handlerAppID];
  735.     }
  736.  
  737.     // Finally, remove any old handler apps that aren't being used anymore,
  738.     // and if those handler apps aren't being used by any other type either,
  739.     // then completely remove their record from the datastore so we don't
  740.     // leave it clogged up with information about handler apps we don't care
  741.     // about anymore.
  742.     for (let handlerAppID in currentHandlerApps) {
  743.       this._removeResourceAssertion(infoID, NC_POSSIBLE_APP, handlerAppID);
  744.       if (!this._existsResourceTarget(NC_POSSIBLE_APP, handlerAppID))
  745.         this._removeAssertions(handlerAppID);
  746.     }
  747.   },
  748.  
  749.   /**
  750.    * Store the given handler app.
  751.    *
  752.    * Note: the reason this method takes the ID of the handler app in a param
  753.    * is that the ID is different than it usually is when the handler app
  754.    * in question is a preferred handler app, so this method can't just derive
  755.    * the ID of the handler app by calling _getPossibleHandlerAppID, its callers
  756.    * have to do that for it.
  757.    *
  758.    * @param aHandlerAppID {string}        the ID of the handler app to store
  759.    * @param aHandlerApp   {nsIHandlerApp} the handler app to store
  760.    */
  761.   _storeHandlerApp: function HS__storeHandlerApp(aHandlerAppID, aHandlerApp) {
  762.     aHandlerApp.QueryInterface(Ci.nsIHandlerApp);
  763.     this._setLiteral(aHandlerAppID, NC_PRETTY_NAME, aHandlerApp.name);
  764.  
  765.     // In the case of the preferred handler, the handler ID could have been
  766.     // used to refer to a different kind of handler in the past (i.e. either
  767.     // a local hander or a web handler), so if the new handler is a local
  768.     // handler, then we remove any web handler properties and vice versa.
  769.     // This is unnecessary but harmless for possible handlers.
  770.  
  771.     if (aHandlerApp instanceof Ci.nsILocalHandlerApp) {
  772.       this._setLiteral(aHandlerAppID, NC_PATH, aHandlerApp.executable.path);
  773.       this._removeTarget(aHandlerAppID, NC_URI_TEMPLATE);
  774.     }
  775.     else {
  776.       aHandlerApp.QueryInterface(Ci.nsIWebHandlerApp);
  777.       this._setLiteral(aHandlerAppID, NC_URI_TEMPLATE, aHandlerApp.uriTemplate);
  778.       this._removeTarget(aHandlerAppID, NC_PATH);
  779.     }
  780.   },
  781.  
  782.   _storeAlwaysAsk: function HS__storeAlwaysAsk(aHandlerInfo) {
  783.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  784.     this._setLiteral(infoID,
  785.                      NC_ALWAYS_ASK,
  786.                      aHandlerInfo.alwaysAskBeforeHandling ? "true" : "false");
  787.   },
  788.  
  789.  
  790.   //**************************************************************************//
  791.   // Convenience Getters
  792.  
  793.   // Observer Service
  794.   __observerSvc: null,
  795.   get _observerSvc() {
  796.     if (!this.__observerSvc)
  797.       this.__observerSvc =
  798.         Cc["@mozilla.org/observer-service;1"].
  799.         getService(Ci.nsIObserverService);
  800.     return this.__observerSvc;
  801.   },
  802.  
  803.   // Directory Service
  804.   __dirSvc: null,
  805.   get _dirSvc() {
  806.     if (!this.__dirSvc)
  807.       this.__dirSvc =
  808.         Cc["@mozilla.org/file/directory_service;1"].
  809.         getService(Ci.nsIProperties);
  810.     return this.__dirSvc;
  811.   },
  812.  
  813.   // MIME Service
  814.   __mimeSvc: null,
  815.   get _mimeSvc() {
  816.     if (!this.__mimeSvc)
  817.       this.__mimeSvc =
  818.         Cc["@mozilla.org/mime;1"].
  819.         getService(Ci.nsIMIMEService);
  820.     return this.__mimeSvc;
  821.   },
  822.  
  823.   // Protocol Service
  824.   __protocolSvc: null,
  825.   get _protocolSvc() {
  826.     if (!this.__protocolSvc)
  827.       this.__protocolSvc =
  828.         Cc["@mozilla.org/uriloader/external-protocol-service;1"].
  829.         getService(Ci.nsIExternalProtocolService);
  830.     return this.__protocolSvc;
  831.   },
  832.  
  833.   // RDF Service
  834.   __rdf: null,
  835.   get _rdf() {
  836.     if (!this.__rdf)
  837.       this.__rdf = Cc["@mozilla.org/rdf/rdf-service;1"].
  838.                    getService(Ci.nsIRDFService);
  839.     return this.__rdf;
  840.   },
  841.  
  842.   // RDF Container Utils
  843.   __containerUtils: null,
  844.   get _containerUtils() {
  845.     if (!this.__containerUtils)
  846.       this.__containerUtils = Cc["@mozilla.org/rdf/container-utils;1"].
  847.                               getService(Ci.nsIRDFContainerUtils);
  848.     return this.__containerUtils;
  849.   },
  850.  
  851.   // RDF datasource containing content handling config (i.e. mimeTypes.rdf)
  852.   __ds: null,
  853.   get _ds() {
  854.     if (!this.__ds) {
  855.       var file = this._dirSvc.get("UMimTyp", Ci.nsIFile);
  856.       // FIXME: make this a memoizing getter if we use it anywhere else.
  857.       var ioService = Cc["@mozilla.org/network/io-service;1"].
  858.                       getService(Ci.nsIIOService);
  859.       var fileHandler = ioService.getProtocolHandler("file").
  860.                         QueryInterface(Ci.nsIFileProtocolHandler);
  861.       this.__ds =
  862.         this._rdf.GetDataSourceBlocking(fileHandler.getURLSpecFromFile(file));
  863.     }
  864.  
  865.     return this.__ds;
  866.   },
  867.  
  868.  
  869.   //**************************************************************************//
  870.   // Datastore Utils
  871.  
  872.   /**
  873.    * Get the string identifying whether this is a MIME or a protocol handler.
  874.    * This string is used in the URI IDs of various RDF properties.
  875.    * 
  876.    * @param aHandlerInfo {nsIHandlerInfo} the handler for which to get the class
  877.    * 
  878.    * @returns {string} the class
  879.    */
  880.   _getClass: function HS__getClass(aHandlerInfo) {
  881.     if (aHandlerInfo instanceof Ci.nsIMIMEInfo)
  882.       return CLASS_MIMEINFO;
  883.     else
  884.       return CLASS_PROTOCOLINFO;
  885.   },
  886.  
  887.   /**
  888.    * Return the unique identifier for a content type record, which stores
  889.    * the value field plus a reference to the content type's handler info record.
  890.    *
  891.    * |urn:<class>:<type>|
  892.    *
  893.    * XXX: should this be a property of nsIHandlerInfo?
  894.    *
  895.    * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
  896.    * @param aType  {string} the type (a MIME type or protocol scheme)
  897.    *
  898.    * @returns {string} the ID
  899.    */
  900.   _getTypeID: function HS__getTypeID(aClass, aType) {
  901.     return "urn:" + aClass + ":" + aType;
  902.   },
  903.  
  904.   /**
  905.    * Return the unique identifier for a handler info record, which stores
  906.    * the preferredAction and alwaysAsk fields plus a reference to the preferred
  907.    * handler app.  Roughly equivalent to the nsIHandlerInfo interface.
  908.    *
  909.    * |urn:<class>:handler:<type>|
  910.    *
  911.    * FIXME: the type info record should be merged into the type record,
  912.    * since there's a one to one relationship between them, and this record
  913.    * merely stores additional attributes of a content type.
  914.    *
  915.    * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
  916.    * @param aType  {string} the type (a MIME type or protocol scheme)
  917.    *
  918.    * @returns {string} the ID
  919.    */
  920.   _getInfoID: function HS__getInfoID(aClass, aType) {
  921.     return "urn:" + aClass + ":handler:" + aType;
  922.   },
  923.  
  924.   /**
  925.    * Return the unique identifier for a preferred handler record, which stores
  926.    * information about the preferred handler for a given content type, including
  927.    * its human-readable name and the path to its executable (for a local app)
  928.    * or its URI template (for a web app).
  929.    * 
  930.    * |urn:<class>:externalApplication:<type>|
  931.    *
  932.    * XXX: should this be a property of nsIHandlerApp?
  933.    *
  934.    * FIXME: this should be an arbitrary ID, and we should retrieve it from
  935.    * the datastore for a given content type via the NC:ExternalApplication
  936.    * property rather than looking for a specific ID, so a handler doesn't
  937.    * have to change IDs when it goes from being a possible handler to being
  938.    * the preferred one (once we support possible handlers).
  939.    * 
  940.    * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
  941.    * @param aType  {string} the type (a MIME type or protocol scheme)
  942.    * 
  943.    * @returns {string} the ID
  944.    */
  945.   _getPreferredHandlerID: function HS__getPreferredHandlerID(aClass, aType) {
  946.     return "urn:" + aClass + ":externalApplication:" + aType;
  947.   },
  948.  
  949.   /**
  950.    * Return the unique identifier for a handler app record, which stores
  951.    * information about a possible handler for one or more content types,
  952.    * including its human-readable name and the path to its executable (for a
  953.    * local app) or its URI template (for a web app).
  954.    *
  955.    * Note: handler app IDs for preferred handlers are different.  For those,
  956.    * see the _getPreferredHandlerID method.
  957.    *
  958.    * @param aHandlerApp  {nsIHandlerApp}   the handler app object
  959.    */
  960.   _getPossibleHandlerAppID: function HS__getPossibleHandlerAppID(aHandlerApp) {
  961.     var handlerAppID = "urn:handler:";
  962.  
  963.     if (aHandlerApp instanceof Ci.nsILocalHandlerApp)
  964.       handlerAppID += "local:" + aHandlerApp.executable.path;
  965.     else {
  966.       aHandlerApp.QueryInterface(Ci.nsIWebHandlerApp);
  967.       handlerAppID += "web:" + aHandlerApp.uriTemplate;
  968.     }
  969.  
  970.     return handlerAppID;
  971.   },
  972.  
  973.   /**
  974.    * Get the list of types for the given class, creating the list if it doesn't
  975.    * already exist. The class can be either CLASS_MIMEINFO or CLASS_PROTOCOLINFO
  976.    * (i.e. the result of a call to _getClass).
  977.    * 
  978.    * |urn:<class>s|
  979.    * |urn:<class>s:root|
  980.    * 
  981.    * @param aClass {string} the class for which to retrieve a list of types
  982.    *
  983.    * @returns {nsIRDFContainer} the list of types
  984.    */
  985.   _ensureAndGetTypeList: function HS__ensureAndGetTypeList(aClass) {
  986.     var source = this._rdf.GetResource("urn:" + aClass + "s");
  987.     var property =
  988.       this._rdf.GetResource(aClass == CLASS_MIMEINFO ? NC_MIME_TYPES
  989.                                                      : NC_PROTOCOL_SCHEMES);
  990.     var target = this._rdf.GetResource("urn:" + aClass + "s:root");
  991.  
  992.     // Make sure we have an arc from the source to the target.
  993.     if (!this._ds.HasAssertion(source, property, target, true))
  994.       this._ds.Assert(source, property, target, true);
  995.  
  996.     // Make sure the target is a container.
  997.     if (!this._containerUtils.IsContainer(this._ds, target))
  998.       this._containerUtils.MakeSeq(this._ds, target);
  999.  
  1000.     // Get the type list as an RDF container.
  1001.     var typeList = Cc["@mozilla.org/rdf/container;1"].
  1002.                    createInstance(Ci.nsIRDFContainer);
  1003.     typeList.Init(this._ds, target);
  1004.  
  1005.     return typeList;
  1006.   },
  1007.  
  1008.   /**
  1009.    * Make sure there are records in the datasource for the given content type
  1010.    * by creating them if they don't already exist.  We have to do this before
  1011.    * storing any specific data, because we can't assume the presence
  1012.    * of the records (the nsIHandlerInfo object might have been created
  1013.    * from the OS), and the records have to all be there in order for the helper
  1014.    * app service to properly construct an nsIHandlerInfo object for the type.
  1015.    *
  1016.    * Based on old downloadactions.js::_ensureMIMERegistryEntry.
  1017.    *
  1018.    * @param aHandlerInfo {nsIHandlerInfo} the type to make sure has a record
  1019.    */
  1020.   _ensureRecordsForType: function HS__ensureRecordsForType(aHandlerInfo) {
  1021.     // Get the list of types.
  1022.     var typeList = this._ensureAndGetTypeList(this._getClass(aHandlerInfo));
  1023.  
  1024.     // If there's already a record in the datastore for this type, then we
  1025.     // don't need to do anything more.
  1026.     var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  1027.     var type = this._rdf.GetResource(typeID);
  1028.     if (typeList.IndexOf(type) != -1)
  1029.       return;
  1030.  
  1031.     // Create a basic type record for this type.
  1032.     typeList.AppendElement(type);
  1033.     this._setLiteral(typeID, NC_VALUE, aHandlerInfo.type);
  1034.     
  1035.     // Create a basic info record for this type.
  1036.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  1037.     this._setLiteral(infoID, NC_ALWAYS_ASK, "false");
  1038.     this._setResource(typeID, NC_HANDLER_INFO, infoID);
  1039.     // XXX Shouldn't we set preferredAction to useSystemDefault?
  1040.     // That's what it is if there's no record in the datastore; why should it
  1041.     // change to useHelperApp just because we add a record to the datastore?
  1042.     
  1043.     // Create a basic preferred handler record for this type.
  1044.     // XXX Not sure this is necessary, since preferred handlers are optional,
  1045.     // and nsExternalHelperAppService::FillHandlerInfoForTypeFromDS doesn't seem
  1046.     // to require the record , but downloadactions.js::_ensureMIMERegistryEntry
  1047.     // used to create it, so we'll do the same.
  1048.     var preferredHandlerID =
  1049.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  1050.     this._setLiteral(preferredHandlerID, NC_PATH, "");
  1051.     this._setResource(infoID, NC_PREFERRED_APP, preferredHandlerID);
  1052.   },
  1053.  
  1054.   /**
  1055.    * Append known handlers of the given class to the given array.  The class
  1056.    * can be either CLASS_MIMEINFO or CLASS_PROTOCOLINFO.
  1057.    *
  1058.    * @param aHandlers   {array} the array of handlers to append to
  1059.    * @param aClass      {string} the class for which to append handlers
  1060.    */
  1061.   _appendHandlers: function HS__appendHandlers(aHandlers, aClass) {
  1062.     var typeList = this._ensureAndGetTypeList(aClass);
  1063.     var enumerator = typeList.GetElements();
  1064.  
  1065.     while (enumerator.hasMoreElements()) {
  1066.       var element = enumerator.getNext();
  1067.       
  1068.       // This should never happen.  If it does, that means our datasource
  1069.       // is corrupted with type list entries that point to literal values
  1070.       // instead of resources.  If it does happen, let's just do our best
  1071.       // to recover by ignoring this entry and moving on to the next one.
  1072.       if (!(element instanceof Ci.nsIRDFResource))
  1073.         continue;
  1074.  
  1075.       // Get the value of the element's NC:value property, which contains
  1076.       // the MIME type or scheme for which we're retrieving a handler info.
  1077.       var type = this._getValue(element.ValueUTF8, NC_VALUE);
  1078.       if (!type)
  1079.         continue;
  1080.  
  1081.       var handler;
  1082.       if (typeList.Resource.ValueUTF8 == "urn:mimetypes:root")
  1083.         handler = this._mimeSvc.getFromTypeAndExtension(type, null);
  1084.       else
  1085.         handler = this._protocolSvc.getProtocolHandlerInfo(type);
  1086.  
  1087.       aHandlers.appendElement(handler, false);
  1088.     }
  1089.   },
  1090.  
  1091.   /**
  1092.    * Whether or not a property of an RDF source has a value.
  1093.    *
  1094.    * @param sourceURI   {string}  the URI of the source
  1095.    * @param propertyURI {string}  the URI of the property
  1096.    * @returns           {boolean} whether or not the property has a value
  1097.    */
  1098.   _hasValue: function HS__hasValue(sourceURI, propertyURI) {
  1099.     var source = this._rdf.GetResource(sourceURI);
  1100.     var property = this._rdf.GetResource(propertyURI);
  1101.     return this._ds.hasArcOut(source, property);
  1102.   },
  1103.  
  1104.   /**
  1105.    * Get the value of a property of an RDF source.
  1106.    *
  1107.    * @param sourceURI   {string} the URI of the source
  1108.    * @param propertyURI {string} the URI of the property
  1109.    * @returns           {string} the value of the property
  1110.    */
  1111.   _getValue: function HS__getValue(sourceURI, propertyURI) {
  1112.     var source = this._rdf.GetResource(sourceURI);
  1113.     var property = this._rdf.GetResource(propertyURI);
  1114.  
  1115.     var target = this._ds.GetTarget(source, property, true);
  1116.  
  1117.     if (!target)
  1118.       return null;
  1119.     
  1120.     if (target instanceof Ci.nsIRDFResource)
  1121.       return target.ValueUTF8;
  1122.  
  1123.     if (target instanceof Ci.nsIRDFLiteral)
  1124.       return target.Value;
  1125.  
  1126.     return null;
  1127.   },
  1128.  
  1129.   /**
  1130.    * Get all targets for the property of an RDF source.
  1131.    *
  1132.    * @param sourceURI   {string} the URI of the source
  1133.    * @param propertyURI {string} the URI of the property
  1134.    * 
  1135.    * @returns {nsISimpleEnumerator} an enumerator of targets
  1136.    */
  1137.   _getTargets: function HS__getTargets(sourceURI, propertyURI) {
  1138.     var source = this._rdf.GetResource(sourceURI);
  1139.     var property = this._rdf.GetResource(propertyURI);
  1140.  
  1141.     return this._ds.GetTargets(source, property, true);
  1142.   },
  1143.  
  1144.   /**
  1145.    * Set a property of an RDF source to a literal value.
  1146.    *
  1147.    * @param sourceURI   {string} the URI of the source
  1148.    * @param propertyURI {string} the URI of the property
  1149.    * @param value       {string} the literal value
  1150.    */
  1151.   _setLiteral: function HS__setLiteral(sourceURI, propertyURI, value) {
  1152.     var source = this._rdf.GetResource(sourceURI);
  1153.     var property = this._rdf.GetResource(propertyURI);
  1154.     var target = this._rdf.GetLiteral(value);
  1155.     
  1156.     this._setTarget(source, property, target);
  1157.   },
  1158.  
  1159.   /**
  1160.    * Set a property of an RDF source to a resource target.
  1161.    *
  1162.    * @param sourceURI   {string} the URI of the source
  1163.    * @param propertyURI {string} the URI of the property
  1164.    * @param targetURI   {string} the URI of the target
  1165.    */
  1166.   _setResource: function HS__setResource(sourceURI, propertyURI, targetURI) {
  1167.     var source = this._rdf.GetResource(sourceURI);
  1168.     var property = this._rdf.GetResource(propertyURI);
  1169.     var target = this._rdf.GetResource(targetURI);
  1170.     
  1171.     this._setTarget(source, property, target);
  1172.   },
  1173.  
  1174.   /**
  1175.    * Assert an arc into the RDF datasource if there is no arc with the given
  1176.    * source and property; otherwise, if there is already an existing arc,
  1177.    * change it to point to the given target. _setLiteral and _setResource
  1178.    * call this after converting their string arguments into resources
  1179.    * and literals, and most callers should call one of those two methods
  1180.    * instead of this one.
  1181.    *
  1182.    * @param source    {nsIRDFResource}  the source
  1183.    * @param property  {nsIRDFResource}  the property
  1184.    * @param target    {nsIRDFNode}      the target
  1185.    */
  1186.   _setTarget: function HS__setTarget(source, property, target) {
  1187.     if (this._ds.hasArcOut(source, property)) {
  1188.       var oldTarget = this._ds.GetTarget(source, property, true);
  1189.       this._ds.Change(source, property, oldTarget, target);
  1190.     }
  1191.     else
  1192.       this._ds.Assert(source, property, target, true);
  1193.   },
  1194.  
  1195.   /**
  1196.    * Assert that a property of an RDF source has a resource target.
  1197.    * 
  1198.    * The difference between this method and _setResource is that this one adds
  1199.    * an assertion even if one already exists, which allows its callers to make
  1200.    * sets of assertions (i.e. to set a property to multiple targets).
  1201.    *
  1202.    * @param sourceURI   {string} the URI of the source
  1203.    * @param propertyURI {string} the URI of the property
  1204.    * @param targetURI   {string} the URI of the target
  1205.    */
  1206.   _addResourceAssertion: function HS__addResourceAssertion(sourceURI,
  1207.                                                            propertyURI,
  1208.                                                            targetURI) {
  1209.     var source = this._rdf.GetResource(sourceURI);
  1210.     var property = this._rdf.GetResource(propertyURI);
  1211.     var target = this._rdf.GetResource(targetURI);
  1212.     
  1213.     this._ds.Assert(source, property, target, true);
  1214.   },
  1215.  
  1216.   /**
  1217.    * Remove an assertion with a resource target.
  1218.    *
  1219.    * @param sourceURI   {string} the URI of the source
  1220.    * @param propertyURI {string} the URI of the property
  1221.    * @param targetURI   {string} the URI of the target
  1222.    */
  1223.   _removeResourceAssertion: function HS__removeResourceAssertion(sourceURI,
  1224.                                                                  propertyURI,
  1225.                                                                  targetURI) {
  1226.     var source = this._rdf.GetResource(sourceURI);
  1227.     var property = this._rdf.GetResource(propertyURI);
  1228.     var target = this._rdf.GetResource(targetURI);
  1229.  
  1230.     this._ds.Unassert(source, property, target);
  1231.   },
  1232.  
  1233.   /**
  1234.    * Whether or not a property of an RDF source has a given resource target.
  1235.    * 
  1236.    * @param sourceURI   {string} the URI of the source
  1237.    * @param propertyURI {string} the URI of the property
  1238.    * @param targetURI   {string} the URI of the target
  1239.    *
  1240.    * @returns {boolean} whether or not there is such an assertion
  1241.    */
  1242.   _hasResourceAssertion: function HS__hasResourceAssertion(sourceURI,
  1243.                                                            propertyURI,
  1244.                                                            targetURI) {
  1245.     var source = this._rdf.GetResource(sourceURI);
  1246.     var property = this._rdf.GetResource(propertyURI);
  1247.     var target = this._rdf.GetResource(targetURI);
  1248.  
  1249.     return this._ds.HasAssertion(source, property, target, true);
  1250.   },
  1251.  
  1252.   /**
  1253.    * Whether or not a property of an RDF source has a given literal value.
  1254.    * 
  1255.    * @param sourceURI   {string} the URI of the source
  1256.    * @param propertyURI {string} the URI of the property
  1257.    * @param value       {string} the literal value
  1258.    *
  1259.    * @returns {boolean} whether or not there is such an assertion
  1260.    */
  1261.   _hasLiteralAssertion: function HS__hasLiteralAssertion(sourceURI,
  1262.                                                          propertyURI,
  1263.                                                          value) {
  1264.     var source = this._rdf.GetResource(sourceURI);
  1265.     var property = this._rdf.GetResource(propertyURI);
  1266.     var target = this._rdf.GetLiteral(value);
  1267.  
  1268.     return this._ds.HasAssertion(source, property, target, true);
  1269.   },
  1270.  
  1271.   /**
  1272.    * Whether or not there is an RDF source that has the given property set to
  1273.    * the given literal value.
  1274.    * 
  1275.    * @param propertyURI {string} the URI of the property
  1276.    * @param value       {string} the literal value
  1277.    *
  1278.    * @returns {boolean} whether or not there is a source
  1279.    */
  1280.   _existsLiteralTarget: function HS__existsLiteralTarget(propertyURI, value) {
  1281.     var property = this._rdf.GetResource(propertyURI);
  1282.     var target = this._rdf.GetLiteral(value);
  1283.  
  1284.     return this._ds.hasArcIn(target, property);
  1285.   },
  1286.  
  1287.   /**
  1288.    * Get the source for a property set to a given literal value.
  1289.    *
  1290.    * @param propertyURI {string} the URI of the property
  1291.    * @param value       {string} the literal value
  1292.    */
  1293.   _getSourceForLiteral: function HS__getSourceForLiteral(propertyURI, value) {
  1294.     var property = this._rdf.GetResource(propertyURI);
  1295.     var target = this._rdf.GetLiteral(value);
  1296.  
  1297.     var source = this._ds.GetSource(property, target, true);
  1298.     if (source)
  1299.       return source.ValueUTF8;
  1300.  
  1301.     return null;
  1302.   },
  1303.  
  1304.   /**
  1305.    * Whether or not there is an RDF source that has the given property set to
  1306.    * the given resource target.
  1307.    * 
  1308.    * @param propertyURI {string} the URI of the property
  1309.    * @param targetURI   {string} the URI of the target
  1310.    *
  1311.    * @returns {boolean} whether or not there is a source
  1312.    */
  1313.   _existsResourceTarget: function HS__existsResourceTarget(propertyURI,
  1314.                                                            targetURI) {
  1315.     var property = this._rdf.GetResource(propertyURI);
  1316.     var target = this._rdf.GetResource(targetURI);
  1317.  
  1318.     return this._ds.hasArcIn(target, property);
  1319.   },
  1320.  
  1321.   /**
  1322.    * Remove a property of an RDF source.
  1323.    *
  1324.    * @param sourceURI   {string} the URI of the source
  1325.    * @param propertyURI {string} the URI of the property
  1326.    */
  1327.   _removeTarget: function HS__removeTarget(sourceURI, propertyURI) {
  1328.     var source = this._rdf.GetResource(sourceURI);
  1329.     var property = this._rdf.GetResource(propertyURI);
  1330.  
  1331.     if (this._ds.hasArcOut(source, property)) {
  1332.       var target = this._ds.GetTarget(source, property, true);
  1333.       this._ds.Unassert(source, property, target);
  1334.     }
  1335.   },
  1336.  
  1337.  /**
  1338.   * Remove all assertions about a given RDF source.
  1339.   *
  1340.   * Note: not recursive.  If some assertions point to other resources,
  1341.   * and you want to remove assertions about those resources too, you need
  1342.   * to do so manually.
  1343.   *
  1344.   * @param sourceURI {string} the URI of the source
  1345.   */
  1346.   _removeAssertions: function HS__removeAssertions(sourceURI) {
  1347.     var source = this._rdf.GetResource(sourceURI);
  1348.     var properties = this._ds.ArcLabelsOut(source);
  1349.  
  1350.     while (properties.hasMoreElements()) {
  1351.       let property = properties.getNext();
  1352.       let targets = this._ds.GetTargets(source, property, true);
  1353.       while (targets.hasMoreElements()) {
  1354.         let target = targets.getNext();
  1355.         this._ds.Unassert(source, property, target);
  1356.       }
  1357.     }
  1358.   }
  1359.  
  1360. };
  1361.  
  1362.  
  1363. //****************************************************************************//
  1364. // More XPCOM Plumbing
  1365.  
  1366. function NSGetModule(compMgr, fileSpec) {
  1367.   return XPCOMUtils.generateModule([HandlerService]);
  1368. }
  1369.